/****************************************************************************
** Copyright (C) 2017 ZhouLiQiu(zhouliqiu@126.com).
**
** This file is part of the qtc_astyle, It is a plugin for Qt Creator.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#include "astyleactions.h"

#include <utils/qtcassert.h>

#include <QDebug>
#include <QObject>
#include <QCoreApplication>

using namespace Utils;

namespace AStyle {
namespace Internal {

typedef QLatin1String _;

AStyleSettings::AStyleSettings()
{}

AStyleSettings::~AStyleSettings()
{
    qDeleteAll(m_items);
}

void AStyleSettings::insertItem(int code, ValueAction *item,
                              const QString &longName, const QString &shortName)
{
    QTC_ASSERT(!m_items.contains(code), qDebug() << code; return);
    m_items[code] = item;
    if (!longName.isEmpty())
    {
        m_nameToCode[longName] = code;
        m_codeToName[code] = longName;
    }
    if (!shortName.isEmpty())
    {
        m_nameToCode[shortName] = code;
    }
}

void AStyleSettings::readSettings(QSettings *settings)
{
    foreach (ValueAction * item, m_items)
    {
        item->readSettings(settings);
    }
}

void AStyleSettings::writeSettings(QSettings *settings)
{
    foreach (ValueAction * item, m_items)
    {
        item->writeSettings(settings);
    }
}

void AStyleSettings::appendArg(QString str)
{
    if (str.isEmpty())
    {
        return;
    }
    if (!m_strStyle.isEmpty())
    {
        m_strStyle.append(QLatin1String(" "));
    }
    m_strStyle.append(str);
}

QString AStyleSettings::getAsArguments()
{
    m_strStyle.clear();

    int n;
    bool b;

    n = item(Style)->value().toInt();
    switch (n)
    {
    case 1:
        appendArg(QLatin1String("--style=allman"));
        break;
    case 2:
        appendArg(QLatin1String("--style=java"));
        break;
    case 3:
        appendArg(QLatin1String("--style=kr"));
        break;
    case 4:
        appendArg(QLatin1String("--style=stroustrup"));
        break;
    case 5:
        appendArg(QLatin1String("--style=whitesmith"));
        break;
    case 6:
        appendArg(QLatin1String("--style=banner"));
        break;
    case 7:
        appendArg(QLatin1String("--style=gnu"));
        break;
    case 8:
        appendArg(QLatin1String("--style=linux"));
        break;
    case 9:
        appendArg(QLatin1String("--style=horstmann"));
        break;
    case 10:
        appendArg(QLatin1String("--style=1tbs"));
        break;
    case 11:
        appendArg(QLatin1String("--style=pico"));
        break;
    case 12:
        appendArg(QLatin1String("--style=lisp"));
        break;
    case 13:
        appendArg(QLatin1String("--style=google"));
        break;
    default:
        break;
    }

    foreach (ValueAction * item, m_items)
    {
        QString str = item->strStyle();
        if (!str.isEmpty())
        {
            bool bValue = item->value().toBool();
            if (bValue)
            {
                appendArg(str);
            }
        }
    }

    n = item(Tab_SpacePerIndent)->value().toInt();
    b = item(Tab_IndentWithSpace)->value().toBool();
    if (b)
    {
        appendArg(tr("--indent=spaces=%1").arg(n));
    }
    b = item(Tab_IndentWithTab)->value().toBool();
    if (b)
    {
        appendArg(tr("--indent=tab=%1").arg(n));
    }
    b = item(Tab_ForceTab)->value().toBool();
    if (b)
    {
        appendArg(tr("--indent=force-tab=%1").arg(n));
    }
    b = item(Tab_ChangeSpacePerTab)->value().toBool();
    if (b)
    {
        n = item(Tab_SpacePerTab)->value().toInt();
        appendArg(tr("--indent=force-tab-x=%1").arg(n));
    }

    b = item(Indent_MinEnable)->value().toBool();
    if (b)
    {
        n = item(Indent_Min)->value().toInt();
        appendArg(tr("--min-conditional-indent=%1").arg(n));
    }

    b = item(Indent_MaxEnable)->value().toBool();
    if (b)
    {
        n = item(Indent_Max)->value().toInt();
        appendArg(tr("--max-instatement-indent=%1").arg(n * 10 + 40));
    }

    b = item(Pad_AlignPointer_Enable)->value().toBool();
    if (b)
    {
        n = item(Pad_AlignPointer)->value().toInt();
        switch (n)
        {
        case 0:
            appendArg(QLatin1String("--align-pointer=type"));
            break;
        case 1:
            appendArg(QLatin1String("--align-pointer=middle"));
            break;
        case 2:
            appendArg(QLatin1String("--align-pointer=name"));
            break;
        default:
            break;
        }
    }

    b = item(Pad_AlignRef_Enable)->value().toBool();
    if (b)
    {
        n = item(Pad_AlignRef)->value().toInt();
        switch (n)
        {
        case 0:
            appendArg(QLatin1String("--align-reference=none"));
            break;
        case 1:
            appendArg(QLatin1String("--align-reference=type"));
            break;
        case 2:
            appendArg(QLatin1String("--align-reference=middle"));
            break;
        case 3:
            appendArg(QLatin1String("--align-reference=name"));
            break;
        default:
            break;
        }
    }

    b = item(Format_MaxCodeLenEnable)->value().toBool();
    if (b)
    {
        n = item(Format_MaxCodeLen)->value().toInt();
        appendArg(tr("--max-code-length=%1").arg(n * 10 + 50));
        b = item(Format_BreakAfterLogic)->value().toBool();
        if (b)
        {
            appendArg(QLatin1String("--break-after-logical"));
        }
    }

    return m_strStyle;
}

ValueAction *AStyleSettings::item(int code)
{
    QTC_ASSERT(m_items.value(code, 0), qDebug() << "CODE: " << code; return 0);
    return m_items.value(code, 0);
}

ValueAction *AStyleSettings::item(const QString &name)
{
    return m_items.value(m_nameToCode.value(name, -1), 0);
}

ValueAction *createAction(AStyleSettings *instance, int code, const QVariant &value,
                          const QString &settingsKey = QString(),
                          const QString &strStyle = QString(),
                          const QString &shortKey = QString())
{
    ValueAction *item = new ValueAction(instance);
    item->setValue(value);

    item->setSettingsKey(_("AStyle"), settingsKey);
    item->setDefaultValue(value);
    item->setCheckable( value.canConvert<bool>() );
    item->setStrStyle(strStyle);

    instance->insertItem(code, item, settingsKey.toLower(), shortKey);
    return item;
}

AStyleSettings *theAStyleSettings()
{
    static AStyleSettings *s = 0;
    if (s)
    {
        return s;
    }

    s = new AStyleSettings;

    createAction(s, Style, 1, _("Style"));

    createAction(s, Tab_IndentWithSpace, true, _("Tab_IndentWithSpace"));
    createAction(s, Tab_IndentWithTab, false, _("Tab_IndentWithTab"));
    createAction(s, Tab_ForceTab, false, _("Tab_ForceTab"));
    createAction(s, Tab_ChangeSpacePerTab, false, _("Tab_ChangeSpacePerTab"));
    createAction(s, Tab_SpacePerTab, 8, _("Tab_SpacePerTab"));
    createAction(s, Tab_SpacePerIndent, 4, _("Tab_SpacePerIndent"));
    createAction(s, Tab_AttachNamespace, true, _("Tab_AttachNamespace"), _("--attach-namespaces"));
    createAction(s, Tab_AttachClass, false, _("Tab_AttachClass"), _("--attach-classes"));
    createAction(s, Tab_AttachInline, false, _("Tab_AttachInline"), _("--attach-inlines"));
    createAction(s, Tab_AttachExtren, false, _("Tab_AttachExtren"), _("--attach-extern-c"));

    createAction(s, Indent_ClassBlocks, false, _("Indent_ClassBlocks"), _("--indent-classes"));
    createAction(s, Indent_AccessModifiers, false, _("Indent_AccessModifiers"), _("--indent-modifiers"));
    createAction(s, Indent_SwitchBlocks, false, _("Indent_SwitchBlocks"), _("--indent-switches"));
    createAction(s, Indent_CaseBlocks, false, _("Indent_CaseBlocks"), _("--indent-cases"));
    createAction(s, Indent_Namespaces, false, _("Indent_Namespaces"), _("--indent-namespaces"));
    createAction(s, Indent_Labels, false, _("Indent_Labels"), _("--indent-labels"));
    createAction(s, Indent_Predefine, false, _("Indent_Predefine"), _("--indent-preproc-define"));
    createAction(s, Indent_PreCon, false, _("Indent_PreCon"), _("--indent-preproc-cond"));
    createAction(s, Indent_ColumnOne, true, _("Indent_ColumnOne"), _("--indent-col1-comments"));
    createAction(s, Indent_Min, 0, _("Indent_Min"));
    createAction(s, Indent_MinEnable, false, _("Indent_MinEnable"));
    createAction(s, Indent_Max, 0, _("Indent_Max"));
    createAction(s, Indent_MaxEnable, false, _("Indent_MaxEnable"));

    createAction(s, Pad_BreakHeader, false, _("Pad_BreakHeader"), _("--break-blocks"));
    createAction(s, Pad_BreakAll, false, _("Pad_BreakAll"), _("--break-blocks=all"));
    createAction(s, Pad_Operator, true, _("Pad_Operator"), _("--pad-oper"));
    createAction(s, Pad_ParensOutside, false, _("Pad_ParensOutside"), _("--pad-paren-out"));
    createAction(s, Pad_ParensInside, false, _("Pad_ParensInside"), _("--pad-paren-in"));
    createAction(s, Pad_FirstParenOutside, false, _("Pad_FirstParenOutside"), _("--pad-first-paren-out"));
    createAction(s, Pad_Header, true, _("Pad_Header"), _("--pad-header"));
    createAction(s, Pad_Unpad, false, _("Pad_Unpad"), _("--unpad-paren"));
    createAction(s, Pad_Delete, false, _("Pad_Delete"), _("--delete-empty-lines"));
    createAction(s, Pad_Fill, false, _("Pad_Fill"), _("--fill-empty-lines"));
    createAction(s, Pad_AlignPointer_Enable, false, _("Pad_AlignPointer_NoChange"));
    createAction(s, Pad_AlignPointer, 0, _("Pad_AlignPointer"));
    createAction(s, Pad_AlignRef_Enable, false, _("Pad_AlignRef_NoChange"));
    createAction(s, Pad_AlignRef, 0, _("Pad_AlignRef_Type"));

    createAction(s, Format_BreakClosing, false, _("Format_BreakClosing"), _("--break-closing-brackets"));
    createAction(s, Format_BreakElseIf, false, _("Format_BreakElseIf"), _("--break-elseifs"));
    createAction(s, Format_AddBracket, true, _("Format_AddBracket"), _("--add-brackets"));
    createAction(s, Format_AddOneBracket, false, _("Format_AddOneBracket"), _("--add-one-line-brackets"));
    createAction(s, Format_RemoveBracket, false, _("Format_RemoveBracket"), _("--remove-brackets"));
    createAction(s, Format_KeepOneLineBlocks, false, _("Format_KeepOneLineBlocks"), _("--keep-one-line-blocks"));
    createAction(s, Format_KeepOneLineStatement, false, _("Format_KeepOneLineStatement"), _("--keep-one-line-statements"));
    createAction(s, Format_ConvertTab, true, _("Format_ConvertTab"), _("--convert-tabs"));
    createAction(s, Format_CloseTemplate, false, _("Format_CloseTemplate"), _("--close-templates"));
    createAction(s, Format_RemoveCommentPrefix, false, _("Format_RemoveCommentPrefix"), _("--remove-comment-prefix"));
    createAction(s, Format_MaxCodeLen, 4, _("Format_MaxCodeLen"));
    createAction(s, Format_MaxCodeLenEnable, false, _("Format_MaxCodeLenEnable"));
    createAction(s, Format_BreakAfterLogic, false, _("Format_BreakAfterLogic"));

    return s;
}

ValueAction *theAStyleSetting(int code)
{
    return theAStyleSettings()->item(code);
}

} // namespace Internal
} // namespace AStyle
